import { useImperativeHandle, useState, useEffect, useRef, useCallback } from 'react'
/* -------------------------------------------------------- antd -- */
import { Table } from 'antd'
// import { VList } from 'virtuallist-antd'
/* -------------------------------------------------------- Sync Function -- */
import Utils from './utils/tool'
/* -------------------------------------------------------- Declare -- */
const { $fn, $config, $http, $lazy, $ } = window
/* -------------------------------------------------------- Async Component -- */
const Pagination = $lazy.load(()=>import('@antd/pagination'))
const Empty = $lazy.load(()=>import('@antd/empty'))
const Loading = $lazy.load(()=>import('@antd/loading'))
const Controls = $lazy.load(()=>import('#cpt/temp/Controls'))
// const ResizeableTitle = $lazy.load(()=>import('./resizeable/cpt/ResizeableTitle'))
/* -------------------------------------------------------- 设置组件之间的间 -- */
const Index = (props, ref) => {
	let {
		className, 					/** @param {String}				# 盒子 className */
		style, 						/** @param {String}				# 盒子 style */
		// Table
		data=[], 					/** @param {Array}				# 表格数据 */
		cols=[], 					/** @param {Array}				# 表格列数据 */
		components, 				/** @param {Array}				# 自自定义组件 */
		// Pagination
		pag, 						/** @param {Object, Boolean}	# 分页配置，如果为 false 无分页 */
		prefix, 					/** @param {Component}			# 前缀 */
		suffix, 					/** @param {Component}			# 后缀 */
		summary,					/** @param {Function}			# 后缀 */
		rowClassName,
		// Empty
		simple,						/** @param {Boolean}			# 简单图标 */
		// Loding
		loadingSize,				/** @param {Boolean}			# 简单图标 */
		// 自定义
		size='middle',				/** @param {String}				# 表格大小 */
		width='100%',				/** @param {String, Boolean}	# 表格宽度 */
		idStr=$config.pullIdName, 	/** @param {String}				# 表格操作字段名 */
		loading, 					/** @param {Array}				# 加载效果 */
		height, 					/** @param {String,Number}		# 表格高度 */
		minHeight, 					/** @param {String,Number}		# 最小高度 */
		init=true, 					/** @param {Boolean}			# 是否初始化加载 */
		isNoMb, 					/** @param {Boolean}			# 是否距离底部有间距 */
		isIndex, 					/** @param {Boolean}			# 是否用索引值替代 key */
		pb='pb10', 					/** @param {String}				# 底部补白 */
		row, 						/** @param {Object}				# 多选与单选 */
		// keys=[],					/** @param {Array}				# 外部 keys */
		onKeysChange, 				/** @param {Event}				# 外部 keys 改变时 */
		onRow, 						/** @param {Event}				# 多选与单选 */
		// resize, 					/** @param {Boolean}			# 是否允许拖动表格宽度 */
		max=50, 					/** @param {Boolean}			# 超过多少条数据虚拟滚动 */
		rightSlot, 					/** @param {Boolean}			# 左侧控件 */
		// http
		api,						/** @param {String}				# 列表 api */
		ctrApi,						/** @param {String}				# 增删改查四合一 api */
		addApi,						/** @param {String}				# 增 api */
		modApi,						/** @param {String}				# 改 api */
		infoApi,					/** @param {String}				# 查 api */
		delApi,						/** @param {String}				# 删 api */
		clearCache,					/** @param {String, Boolean}	# 要清除的缓存 api */
		paging=true,				/** @param {Boolean}			# 请求接口是否分页 */
		onLoading, 					/** @param {Event}				# 加载时执行 */
		onOk, 						/** @param {Event}				# 请求成功后执行 */
		onUpdate, 					/** @param {Event}				# 数据更新后执行 */
		setName, 					/** @param {Function}			# 自定义数据显示字段 */
		setData,					/** @param {Function}			# 自定义处理所有数据 */
		minus=0,					/** @param {Number}				# 总数据减去多余数据 */
		// 操作按钮
		controls, 					/** @param {Array}				# 操作按钮组 */
		//
		search={}, 					/** @param {Object}				# 搜索配置 */
		// 作为 Form 的元素
		// isRows, 					/** @param {Boolean}			# 暴露参数是否是 rows 数据 */
		isSubmitForm, 				/** @param {Boolean}			# 是否是在提交表单中 */
		isData, 					/** @param {Boolean}			# 是否只有外部数据 */
		isModalTable, 				/** @param {Boolean}			# 表格弹窗 */
		onChange, 					/** @param {Event}				# 表单自动提交事件 */
		value, 						/** @param {Array}				# 接收表单数据 */
		// antd
		antd, 						/** @param {Object}				# antd */
		emptyText, 					/** @param {Object}				# 自定义空数据时提示 */
		global,						/** @param {Function}			# SearchTable 全局传递变量 */
		x,							/** @param {Number}				# 横向宽度 */
		onInit,						/** @param {Function}			# 初始运行 */
		perm,						/** @param {Array}				# 权限组 */
	} = props
	const dataRef = useRef(data)
	idStr = isIndex && idStr == $config.pullIdName ? 'i' : idStr
	const [ ajax, setAjax ] = useState({ data, loading })
	const isAuto = height === 'auto'
	const isPaging = paging !== 0 && paging !== false
	const tableDomRef = useRef()
	const is_virtual = ajax?.data?.length > max
	/* --------------------------- 表单中的 table --------------------------- */
	useEffect(()=>{
		if(isSubmitForm){
			let rs = $fn.isArray(value) ? value: []
			if(row){
				setSelectedRowKeys( [...rs] )
			}else{
				if(!api){
					if(isIndex) rs = (v,i) => ({ ...v, i: i + 1 })
					dataRef.current = rs
					setTimeout(()=>setAjax({ data: [...rs] }), 50)
				}
			}
		}
	},[ row, value ]) // eslint-disable-line
	useEffect(()=>{
		if(isData) {
			let d = $fn.getArray( data )
			if($fn.hasArray(d) && isIndex)  d  = d.map((v,i) => ({ ...v, i: i + 1 }))
			setAjax({ data:d })
			dataRef.current = d
		}
	},[ data, isData ]) // eslint-disable-line
	/* --------------------------- 滚动顶部 --------------------------- */
	const scrollTop = value => {
		if( is_virtual ) return
		const { current } = tableDomRef
		const dom = current?.querySelector?.('.ant-table-body');
		if(dom){
			if($fn.isNumber(value)){
				dom.scrollTop = value
			}else{
				return dom.scrollTop
			}
		}
	}
	const scrollRest = value => {
		if( !isAuto && $fn.isNumber(value) ){ scrollTop( value ) }
	}
	/* --------------------------- ajax --------------------------- */
	const apiRef = useRef(api)
	const fetch = useCallback(async option => {
		let { param, query, arg, scroll, resetEmpty, resetSearch } = option ?? props
		param = $fn.getResult(param)
		if( search.hasNames ){
			param = $fn.getSubmitParam(search.data, param)
		}
		query = $fn.getResult(query)
		arg = $fn.getResult(arg)
		if(arg){
			setAjax.arg = arg
		}
		const apiUrl = option?.api ?? apiRef.current
		apiRef.current = apiUrl
		if(apiUrl){
			onLoading?.( true )
			await $http.paging(setAjax, apiUrl, { param, id:props?.id, query, arg, paging,
				onEnd: () => onLoading?.( false ),
				setName: setName ? setName : (isIndex ? (_,i) => ({ i: i + 1 }) : null),
				setData,
				setParam: search?.setParam,
				resetEmpty,
				resetSearch
			}).then(({ data, rows })=>{
				if(row?.keys){
					setKeys(row.keys)
				}
				if(scroll === 1 || scroll === true) { // 滚动条到顶部
					scrollRest(0)
				}else if( scroll === 2 ){	 // 滚动条到底部
					scrollRest( window.innerHeight )
				} else { // 滚动条不动
					// eslint-disable-line
				}
				dataRef.current = rows ? rows : data
				onOk?.(rows ? rows : data, { param, query })
			})
		}
	},[ ])  // eslint-disable-line
	// 排序
	const onSort = (pagination, filters, sorter) => {
		let type = null
		if(sorter.order === 'ascend'){			// 升序
			type = 'asc'
		}else if(sorter.order === 'descend'){ 	// 降序
			type = 'desc'
		}else{
			type = 0							// 不排序
		}
		const param = type ? {sort:sorter.field, sort_type: type} : null
		if(param){
			setAjax.model = {...setAjax.model, ...param}
		}else{
			delete setAjax.model.sort
			delete setAjax.model.sort_type
		}
		fetch({scroll:true})
	}
	/* --------------------------- 默认请求列表 --------------------------- */
	const onInitFetch = () => {
		const defaultParam = Utils.getParam($fn.getResult(search.data))
		const param 		= $fn.getResult(search?.param || props?.param)
		const query 		= $fn.getResult(search?.query || props?.query)
		fetch({ param:{ ...defaultParam, ...param }, query, scroll:true })
	}
	useEffect(()=>{
		if( init ){
			onInitFetch()
		}
		onInit?.({ tableRef })
	},[ ]) // eslint-disable-line
	/* --------------------------- 处理 cols --------------------------- */
	const getCols = useCallback(cols =>{
		cols = $fn.getResult( cols )
		if( !Array.isArray( cols) ){
			return []
		}
		cols = cols.filter( v => $fn.hasObject(v) )
		const _controls = $fn.getResult(controls)
		if($fn.hasObject(_controls)){
			let controlSize = _controls.size ?? 'small'
			if(size === 'small'){ controlSize = 'mini' }
			const { data, align='center', right=1 } = _controls
			const fixed = right ? 'right' : 'left'
			let stack = null
			if(Array.isArray(perm) && $fn.getUser().role !== 0){
				stack = data({},0, tableRef).map(v => {
					let { mode } = v
					if( ['del', 'delPop'].includes(mode) && !perm.includes('d') ) return null
					else if( mode==='mod' && !perm.includes('u') ) return null
					else if( mode==='add' && !perm.includes('c') ) return null
					else if( ['upload', 'confirm', 'formModal','submit', 'mg_download', 'download'].includes(mode) && !perm.includes('o') ) return null
					else {
						return v.perm && v.perm !== 'a' && !perm.includes(v.perm) ? null : 1
					}
				})
			}
			const ctrl = { title: '操作', width:120, fixed, render:(record, row, index)=>{
				return (
					<Controls
						size 	= { controlSize }
						align 	= { align } 
						data	= { data?.(record, index, tableRef) }
						global 	= { global }
						perm 	= { perm }
						table 	= { { ctrApi, clearCache, delApi, infoApi, modApi, addApi, record, api, idStr, tableRef, onUpdate, index } }
						isTable 
					/>
				)
			}, ..._controls, align:'center' }
			let stack_ = Array.isArray(stack) ? stack.filter(v => !!v ) : null
			if( stack_ === null || (Array.isArray(stack_) && stack_.length > 0)  ){
				return right ?  [ ...cols, ctrl ] : [ ctrl, ...cols]
			}else{
				return cols
			}
		}else{
			return cols ?? []
		}
	},[ controls ]) // eslint-disable-line
	
	const [ col, setCol ] = useState([ ])
	const [ group, setGroup ] = useState()
	
	useEffect( ()=> { 
		if( $fn.hasArray(cols) ) {
			setCol( getCols(cols) )
			setGroup(cols.some( v => v?.children ))
		}
	}, [ cols ] ) // eslint-disable-line
	
	/* --------------------------- 单选与多选 --------------------------- */
	const [ selectedRowKeys, setSelectedRowKeys ] = useState(row?.keys ?? [])
	//
	const checkbox = [ 'checkbox', 'radio' ]
	
	const rowSelection = $fn.isObject(row) && $fn.hasArray(ajax.data) ? {
		columnWidth: 50,
		type: row?.type ? checkbox[row?.type] : checkbox[0],
		selectedRowKeys,
		// 选框改变时执行
		onChange : (keys, rows) => setKeys(keys, rows),
		// onSelect(record, selected, selectedRows){},
		// 全选
		// onSelectAll(selected, rows, changeRows){},
		// 判断行属性
		getCheckboxProps: record => {
			const { disabledKeys } = row ?? {}
			if( $fn.isFunction(disabledKeys) ){
				const disabled = disabledKeys?.(record)
				record.disabled = disabled
				return { disabled }
			}else{
				if( $fn.hasArray(disabledKeys) ){
					const disabled = disabledKeys.includes( record[idStr] )
					record.disabled = disabled
					return { disabled }
				}else{
					return {}
				}
			}
		},
		...row?.antd
	} : null
	// 自定义点击行
	const _onRow = record => ({
		onClick: () => {
			if( $fn.isObject( row ) ){
				if( record.disabled ){ return }
				let keys = selectedRowKeys 
				let { type } = row
				const key = record[idStr]
				
				if(selectedRowKeys.includes(key)){
					const index = keys.findIndex(v => v === key)
					keys.splice(index, 1)
					
					row?.onRowFalse?.(record, tableRef)
				}else{
					const v = record[idStr]
					if(type === 1){
						keys = [ v ]
					}else{
						keys.push( v )
					}
					row?.onRowTrue?.(record, tableRef)
				}
				setKeys(keys)
				row?.onRow?.({ row: record, keys:getKeys(), rows:()=>getRows(keys), tableRef })
				onRow?.( { row: record, keys, rows:getRows, tableRef } )
			}else{
				onRow?.( record )
			}
		}
	})
	// 清除 keys
	const clearKeys = useCallback(() => {
		if ( row ){
			setKeys([]) 
		}
	}, [selectedRowKeys] ) // eslint-disable-line
	// 设置 keys
	const setKeys = useCallback( (keys, rows_rs) => {
		// if( !$fn.hasArray(ajax.data) ) return
		const rows = $fn.hasArray(rows_rs) ? ( ()=> row.type ? rows_rs?.[0] : rows_rs ) : ()=>getRows(keys)
		row.keys = keys
		// row.rows = rows
		setSelectedRowKeys( [...keys] )
		
		onKeysChange?.( [...keys], rows  )
		if(isSubmitForm){ onChange?.( keys ) }
		row?.change?.({ 
			keys:row.type ?  keys?.[0] ?? null : keys, 
			rows, 
			tableRef 
		})
 	}, [ ajax.data ]) // eslint-disable-line
	const getKeys = useCallback(() => {
		if( $fn.isObject(row) ){
			const { type } = row
			return type ? ($fn.hasArray(selectedRowKeys) ? selectedRowKeys[0] : null) : ( selectedRowKeys ?? [] )
		}else{
			return row?.type ? null : []
		}
	},[ selectedRowKeys, row ])
	//设置 rows
	const setRows = rows => setKeys( rows.map( v => v[idStr]) )
	// 获取 rows
	const getRows = useCallback(function(){
		const keys = (arguments.length === 0 ? selectedRowKeys : arguments[0]) ?? []
		const d = ajax.data.filter( v => keys?.includes?.( v[idStr]) )
		return row?.type ? ( d[0] ?? null ) : d
	},[ row, ajax.data, selectedRowKeys ]) // eslint-disable-line
	/* --------------------------- Expose --------------------------- */
	const tableRef = {
		refresh : async option => await fetch({ scroll: true, ...option }),
		search: async option => {
			const {param, query, arg, scroll=true, api} = option ?? {}
			search.query = { ...search?.query, ...query }
			clearKeys()
			await fetch( { param: isPaging ? { ...param, page: 1} : param, query:search.query, arg, scroll, api })
		},
		reset: () => {
			setAjax.model = isPaging ? { page:1, size:ajax.pager?.size ?? $config.paging.pageSize } : {}
			onInitFetch()
		},
		// 清空全部搜索参数，并将数据置空，不搜索
		resetEmpty: async () => {
			clearKeys()
			await fetch( { resetEmpty:true })
		},
		// 清空全部搜索参数并搜索
		resetSearch: async option => {
			clearKeys()
			await fetch( { resetSearch:true, ...option })
		},
		refreshClear: option => {
			clearKeys()
			fetch({ scroll: true, ...option })
		},
		getParam: () => ajax?.pager?.param || {}, 		// 获取所有参数
		getArg: () => setAjax.arg || {}, 				// 获取额外参数
		// 设置选中
		clearKeys,
		keys(){ return arguments.length === 1  ? setKeys( arguments[0] ) : getKeys() },
		rows(){ return arguments.length === 1  ? setRows( arguments[0] ) : getRows() },
		data(){
			if(arguments.length >= 1){
				let d = arguments[0]
				let delay = arguments[1]
				d = Array.isArray(d) ? d : []
				if($fn.hasArray(d) && isIndex)  d = d.map((v,i) => ({ ...v, i: i + 1 }))
				dataRef.current = d.length === 0 ? [] : d
				if( delay ){
					setTimeout(()=>setAjax({ data: [...dataRef.current] })) // 加延迟解决莫名其妙的错误
				}else{
					setAjax({ data: [...dataRef.current] })
				}
			}else{
				return dataRef.current
			}
		},
		setIndexData(d){
			d = Array.isArray(d) ? d : []
			if($fn.hasArray(d) && isIndex)  d  = d.map((v,i) => ({ ...v, i: i + 1 }))
			dataRef.current = d.length === 0 ? [] : d
			setAjax({ data: [...dataRef.current] })
		},
		del(i){
			dataRef.current.splice(i,1)
			if( row ) {
				this.keys().splice(i,1)
				this.keys( this.keys() )
			}
			this.data(dataRef.current)
		},
		// 强制更新
		update(f){ setAjax({ data: f ? $fn.deepCopy(dataRef.current) : [...dataRef.current] }) }
	}
	useImperativeHandle( ref, () => tableRef )
	/* --------------------------- 虚拟滚动 --------------------------- */
	// const wrapRef = useRef()
	// 拖动表格宽
	/*
	const handleResize = index => (e, { size }) => {
		setCol(col => {
			const nextColumns = [...col];
			nextColumns[index] = { ...nextColumns[index], width: size.width }
			return nextColumns
		})
	}
	*/
	// useEffect(()=>{
	// 	if( ajax?.data?.length > 0 ){
	// 		const _component = ajax?.data?.length > max ? { ...VList({height: height ?? wrapRef.current.offsetHeight }), ...components } : {...components}
	// 		/*
	// 		if( resize ){
	// 			_component.header = { cell: ResizeableTitle }
	// 			setCol(col => col.map((col, index) => ({
	// 				...col,
	// 				onHeaderCell: column => {
	// 					const { width, resize } = column
	// 					return resize !== false && resize !== 0  ? {
	// 						width,
	// 						onResize: handleResize(index)
	// 						// style: { cursor: 'move' },
	// 					} : null
	// 				}
	// 			})))
	// 		}
	// 		*/
	// 		setComponent( _component )
	// 	}
	// },[ ajax.data ]) // eslint-disable-line
	const [ y, setY ] = useState(200)
	useEffect(()=>{
		if(is_virtual && tableDomRef.current){
			let dif = group ? 72 : 50;
			if( is_virtual && summary ){
				dif = group ? 72 : 35
			}
			window.onresize = () => {
				const h = tableDomRef.current?.querySelector?.('.ant-table-container')?.clientHeight;
				if( h ) setY( h - dif )
			}
			let len = ajax.data.length
			let time = len <= 300 ? 300 : len + dif
			setTimeout(window.onresize, isModalTable ? time : 0)
		}
	},[ is_virtual, ajax.data, isModalTable, group, summary ])
	
	const _summary = cols => {
		let cpt = null
		if( $fn.isArray(cols)  ){
			if( $fn.isArray(cols[0]) ){
				cpt =  cols.map((p,k) => (
					<Table.Summary.Row key={k}>
						{
							p.map((v,i) => <Table.Summary.Cell key={i} index={i} colSpan={ v?.span }>{ v?.slot ? v.slot : v }</Table.Summary.Cell>)
						}
					</Table.Summary.Row>
				))
			}else{
				cpt = (
					<Table.Summary.Row>
						{
							cols.map((v,i) => <Table.Summary.Cell key={i} index={i} colSpan={ v?.span }>{ v?.slot ? v.slot : v }</Table.Summary.Cell>)
						}
					</Table.Summary.Row>
				)
			}
		}else if( $fn.isObject(cols) && !cols.props ){
			cpt = <Table.Summary.Row><Table.Summary.Cell index={0} colSpan={cols.span}>{ cols?.slot  }</Table.Summary.Cell></Table.Summary.Row>
		}else{
			cpt = <Table.Summary.Row><Table.Summary.Cell index={0} colSpan={ col.length+2 }>{ cols  }</Table.Summary.Cell></Table.Summary.Row>
		}
		return <Table.Summary fixed>{cpt}</Table.Summary>
	}
	
	const TableComponent = (
		<Table
			style				= {{zIndex: ajax.loading ? 1 : 0}}
			rowKey				= { r =>  isIndex && idStr == $config.pullIdName ?  r['i'] : r[idStr] }
			columns				= { col }
			dataSource			= { $fn.getArray( ajax.data ) }
			// scroll				= { { y: !$fn.hasArray(ajax.data) || isAuto ? null : true, x: width }}
			scroll				= { is_virtual ? { x: x ? x : 2000, y} : { y: !$fn.hasArray(ajax.data) || isAuto ? null : true, x: width }}
			sticky				= { true }
			pagination			= { false }
			bordered 			= { true }
			rowSelection		= { rowSelection }
			rowClassName 		= { rowClassName }
			onChange 			= { onSort }
			onRow				= { _onRow }
			virtual				= { is_virtual }
			components 			= { components }
			expandRowByClick	= { true }
			summary 			= { summary && ajax.data.length>0 ? () => _summary(summary?.( api ? ajax?.pager || {} : { data:ajax?.data || {} } )) : null }
			{...antd}
		/>
	)
	const loadingComponent = <Loading loading={ajax.loading} size={loadingSize} style={{zIndex:2}}/>
	
	const emptyComponent = <Empty loading={ajax.loading} data={ajax.data} simple={simple} style={{zIndex:1}} msg={ emptyText ? emptyText : (init === false ? '请按条件搜索数据' : '暂无数据') } />
	return (
		<div className={`ex rel ${$.css(ajax?.data?.length === 0, 'oh')} ${$.css(group, 'jzer-table-group')} ${$.css(!isAuto, 'fv')} ${$.css(pag===false, pb)} ${$.css(!props.search && !props.mt,'mt5')} jzer-form-small ${$.css(className)}`} style={{height,...style}}>
			<div ref={tableDomRef} 
				className={`
					jzer-table ex rel
					jzer-table-${size}
					jzer-table-${ simple ? 'simple' : 'no-simple' }
					${ $.css( ((pag===false && !isAuto) || ajax?.data?.length === 0) && !rightSlot,'bbor1') } 
					${ !$fn.hasArray(ajax.data) && !isNoMb ? 'mb10 jzer-table-no-data':'' } 
					${ $.css(!isAuto,'jzer-table-scroll') }
					${ $.css(rightSlot, 'fx') }
					${ $.css(is_virtual && summary, 'jzer-table-virtual') }
				`}
				style={{minHeight: minHeight ||  (simple ? 120 : 180)}}
			>
				{
					rightSlot ? (
						<>
							<div className={`rel ex rbor1 bbor1 fv ${ $.css(((pag===false && !isAuto) || ajax?.data?.length === 0), 'bbor1')}`}>{TableComponent}{loadingComponent}{emptyComponent}</div>
							<div className={`rel fv ml10 ${$.css(rightSlot.className)}`} style={{width:rightSlot.width, ...rightSlot?.style}}>
								{ rightSlot.slot }
							</div>
						</>
					)  : TableComponent
				}
			</div>
			{
				!rightSlot && (
					<>
						{ loadingComponent }
						{ emptyComponent }
					</>
				)
			}
			{
				( $fn.hasArray(ajax.data) && pag !== false && pag !== 0  ) && (
					<div className={`fxm ptb10 ${$.css(!isAuto && !rightSlot,'tbor1')}`}>
						{ $fn.getResult( prefix, api ? ajax.pager : { data:ajax.data }) }
						{
							$fn.isNoFalse(pag?.showTotal) && <div className='g9 f12 nowrap'>共 { (ajax?.pager?.total ? ajax?.pager?.total : ajax.data.length) - minus } 条数据</div>
						}
						{ $fn.getResult( suffix, api ? ajax.pager : { data:ajax.data }) }
						{
							isPaging && (
								<Pagination
									pager		= { ajax.pager } 
									pag			= { pag } 
									className	= { $.css(!prefix,'ex') }
									onChange	= { (current, pageSize) =>{
										fetch({ param:{ page:current, size:pageSize }, scroll:true })
										clearKeys()
									} }
								/>
							)
						}
					</div>
				)
			}
		</div>
	)
}

export default Index